package com.wzw.expression.expression.source;

import com.wzw.expression.expression.operation.Operation;
import com.wzw.expression.expression.parser.AnnotationParser;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * @author Wangzhiwen
 */
public class AnnotationOperationSource extends AbstractOperationSource {

    private final Set<AnnotationParser> annotationParsers = new HashSet<>(4);

    private final boolean publicMethodsOnly;

    public AnnotationOperationSource() {
        this(true);
    }

    public AnnotationOperationSource(boolean publicMethodsOnly) {
        this.publicMethodsOnly = publicMethodsOnly;
    }

    public void addAnnotationParser(AnnotationParser parser) {
        annotationParsers.add(parser);
    }

    @Override
    public boolean isCandidateClass(Class<?> targetClass) {
        for (AnnotationParser parser : this.annotationParsers) {
            if (parser.isCandidateClass(targetClass)) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected Collection<Operation> findOperations(Class<?> clazz) {
        return determineOperations(parser -> parser.parseAnnotations(clazz));
    }

    @Override
    protected Collection<Operation> findOperations(Method method) {
        return determineOperations(parser -> parser.parseAnnotations(method));
    }

    @Nullable
    protected Collection<Operation> determineOperations(OperationProvider provider) {
        Collection<Operation> ops = null;
        for (AnnotationParser parser : this.annotationParsers) {
            Collection<Operation> annOps = provider.getOperations(parser);
            if (annOps != null) {
                if (ops == null) {
                    ops = annOps;
                } else {
                    Collection<Operation> combined = new ArrayList<>(ops.size() + annOps.size());
                    combined.addAll(ops);
                    combined.addAll(annOps);
                    ops = combined;
                }
            }
        }
        return ops;
    }

    @Override
    protected boolean allowPublicMethodsOnly() {
        return publicMethodsOnly;
    }

    @FunctionalInterface
    protected interface OperationProvider {

        @Nullable
        Collection<Operation> getOperations(AnnotationParser parser);

    }
}
